added hooks corresponding to annex.*-command
authorJoey Hess <joeyh@joeyh.name>
Fri, 10 Jan 2025 18:50:49 +0000 (14:50 -0400)
committerJoey Hess <joeyh@joeyh.name>
Fri, 10 Jan 2025 18:54:42 +0000 (14:54 -0400)
* Added freezecontent-annex and thawcontent-annex hooks that
  correspond to the git configs annex.freezecontent and
  annex.thawcontent.
* Added secure-erase-annex hook that corresponds to the git config
  annex.secure-erase-command.
* Added commitmessage-annex hook that corresponds to the git config
  annex.commitmessage-command.
* Added http-headers-annex hook that corresponds to the git config
  annex.http-headers-command.
  that correspond to the post-update-annex and pre-commit-annex hooks.

The use case for these is eg, setting up a git repository that is run in a
container, where the easiest way to provide a script is by putting it in
.git/hooks/, rather than copying it into the container in a way that puts
it in PATH.

This is all the ones that make sense to add for annex.*-config git configs.
annex.youtube-dl-command is not a hook, it's telling git-annex what command
to run. So is annex.shared-sop-command. So omitted those.

May later also want to add hooks corresponding to
`remote.<name>.annex-cost-command` etc.

Sponsored-by: the NIH-funded NICEMAN (ReproNim TR&D3) project
Annex/Branch.hs
Annex/Content/LowLevel.hs
Annex/Hook.hs
Annex/Perms.hs
Annex/Url.hs
CHANGELOG
Git/Hook.hs
doc/git-annex.mdwn
doc/todo/specify_freeze__47__thaw_scripts_relative_to_topdir.mdwn
doc/todo/specify_freeze__47__thaw_scripts_relative_to_topdir/comment_6_6bda77480362b38d6984d38430a1dc17._comment
doc/todo/specify_freeze__47__thaw_scripts_relative_to_topdir/comment_7_8fd25d5d2b802c6ff6ee0a3802d6f1cb._comment [new file with mode: 0644]

index 03adce0886b059078a49f5ce4cbd1f979efda4b0..ce4c3ad85e43801e36794dccdbb2c949d86ba093 100644 (file)
@@ -521,12 +521,10 @@ createMessage :: Annex String
 createMessage = fromMaybe "branch created" <$> getCommitMessage
 
 getCommitMessage :: Annex (Maybe String)
-getCommitMessage = do
-       config <- Annex.getGitConfig
-       case annexCommitMessageCommand config of
-               Nothing -> return (annexCommitMessage config)
-               Just cmd -> catchDefaultIO (annexCommitMessage config) $
-                       Just <$> liftIO (readProcess "sh" ["-c", cmd])
+getCommitMessage = 
+       outputOfAnnexHook commitMessageAnnexHook annexCommitMessageCommand
+               <|>
+       (annexCommitMessage <$> Annex.getGitConfig)
 
 {- Stages the journal, and commits staged changes to the branch. -}
 commit :: String -> Annex ()
index 9d732f6a6e0bfdf19e6f4e6739d498132925931e..69baf199571a379e5b0b402a07e7a05c94a6d0b3 100644 (file)
@@ -10,6 +10,7 @@
 module Annex.Content.LowLevel where
 
 import Annex.Common
+import Annex.Hook
 import Logs.Transfer
 import qualified Annex
 import Utility.DiskFree
@@ -25,11 +26,8 @@ import System.PosixCompat.Files (linkCount)
  - File may or may not be deleted at the end; caller is responsible for
  - making sure it's deleted. -}
 secureErase :: RawFilePath -> Annex ()
-secureErase file = maybe noop go =<< annexSecureEraseCommand <$> Annex.getGitConfig
-  where
-       go basecmd = void $ liftIO $
-               boolSystem "sh" [Param "-c", Param $ gencmd basecmd]
-       gencmd = massReplace [ ("%file", shellEscape (fromRawFilePath file)) ]
+secureErase = void . runAnnexPathHook "%file"
+       secureEraseAnnexHook annexSecureEraseCommand
 
 data LinkedOrCopied = Linked | Copied
 
index deadf871edf93468cc29c632c9910e3d07525475..e4264ce9d52e2aaceb0ab5d0cbc061313e5e625a 100644 (file)
@@ -4,7 +4,7 @@
  - git-annex not change, otherwise removing old hooks using an old
  - version of the script would fail.
  -
- - Copyright 2013-2019 Joey Hess <id@joeyh.name>
+ - Copyright 2013-2025 Joey Hess <id@joeyh.name>
  -
  - Licensed under the GNU AGPL version 3 or higher.
  -}
@@ -48,6 +48,21 @@ preCommitAnnexHook = Git.Hook "pre-commit-annex" "" []
 postUpdateAnnexHook :: Git.Hook
 postUpdateAnnexHook = Git.Hook "post-update-annex" "" []
 
+freezeContentAnnexHook :: Git.Hook
+freezeContentAnnexHook = Git.Hook "freezecontent-annex" "" []
+
+thawContentAnnexHook :: Git.Hook
+thawContentAnnexHook = Git.Hook "thawcontent-annex" "" []
+
+secureEraseAnnexHook :: Git.Hook
+secureEraseAnnexHook = Git.Hook "secure-erase-annex" "" []
+
+commitMessageAnnexHook :: Git.Hook
+commitMessageAnnexHook = Git.Hook "commitmessage-annex" "" []
+
+httpHeadersAnnexHook :: Git.Hook
+httpHeadersAnnexHook = Git.Hook "http-headers-annex" "" []
+
 mkHookScript :: String -> String
 mkHookScript s = unlines
        [ shebang
@@ -69,23 +84,26 @@ hookWarning h msg = do
        warning $ UnquotedString $
                Git.hookName h ++ " hook (" ++ Git.hookFile h r ++ ") " ++ msg
 
-{- Runs a hook. To avoid checking if the hook exists every time,
- - the existing hooks are cached. -}
-runAnnexHook :: Git.Hook -> (GitConfig -> Maybe String) -> Annex ()
-runAnnexHook hook commandcfg = do
+{- To avoid checking if the hook exists every time, the existing hooks
+ - are cached. -}
+doesAnnexHookExist :: Git.Hook -> Annex Bool
+doesAnnexHookExist hook = do
        m <- Annex.getState Annex.existinghooks
        case M.lookup hook m of
-               Just True -> runhook
-               Just False -> runcommandcfg
+               Just exists -> return exists
                Nothing -> do
                        exists <- inRepo $ Git.hookExists hook
                        Annex.changeState $ \s -> s
                                { Annex.existinghooks = M.insert hook exists m }
-                       if exists
-                               then runhook
-                               else runcommandcfg
+                       return exists
+
+runAnnexHook :: Git.Hook -> (GitConfig -> Maybe String) -> Annex ()
+runAnnexHook hook commandcfg = ifM (doesAnnexHookExist hook)
+       ( runhook
+       , runcommandcfg
+       )
   where
-       runhook = unlessM (inRepo $ Git.runHook hook) $ do
+       runhook = unlessM (inRepo $ Git.runHook boolSystem hook []) $ do
                h <- fromRepo $ Git.hookFile hook
                commandfailed h
        runcommandcfg = commandcfg <$> Annex.getGitConfig >>= \case
@@ -94,3 +112,29 @@ runAnnexHook hook commandcfg = do
                                commandfailed command
                Nothing -> noop
        commandfailed c = warning $ UnquotedString $ c ++ " failed"
+
+runAnnexPathHook :: String -> Git.Hook -> (GitConfig -> Maybe String) -> RawFilePath -> Annex Bool
+runAnnexPathHook pathtoken hook commandcfg p = ifM (doesAnnexHookExist hook)
+       ( runhook
+       , runcommandcfg
+       )
+  where
+       runhook = inRepo $ Git.runHook boolSystem hook [ File (fromRawFilePath p) ]
+       runcommandcfg = commandcfg <$> Annex.getGitConfig >>= \case
+               Just basecmd -> liftIO $
+                       boolSystem "sh" [Param "-c", Param $ gencmd basecmd]
+               Nothing -> return True
+       gencmd = massReplace [ (pathtoken, shellEscape (fromRawFilePath p)) ]
+
+outputOfAnnexHook :: Git.Hook -> (GitConfig -> Maybe String) -> Annex (Maybe String)
+outputOfAnnexHook hook commandcfg = ifM (doesAnnexHookExist hook)
+       ( runhook
+       , runcommandcfg
+       )
+  where
+       runhook = inRepo (Git.runHook runhook' hook [])
+       runhook' c ps = Just <$> readProcess c (toCommand ps)
+       runcommandcfg = commandcfg <$> Annex.getGitConfig >>= \case
+               Just command -> liftIO $ 
+                       Just <$> readProcess "sh" ["-c", command]
+               Nothing -> return Nothing
index 56bd35190e67f1cbc80d416d027735d1c0df01dc..03bce4fe830df7cc10d720cbec7aed72c6147c00 100644 (file)
@@ -33,6 +33,7 @@ module Annex.Perms (
 ) where
 
 import Annex.Common
+import Annex.Hook
 import Utility.FileMode
 import Git
 import Git.ConfigTypes
@@ -340,24 +341,24 @@ modifyContentDirWhenExists f a = do
        either throwM return v
 
 hasFreezeHook :: Annex Bool
-hasFreezeHook = isJust . annexFreezeContentCommand <$> Annex.getGitConfig
+hasFreezeHook = 
+       (isJust . annexFreezeContentCommand <$> Annex.getGitConfig)
+               <||>
+       (doesAnnexHookExist freezeContentAnnexHook)
 
 hasThawHook :: Annex Bool
-hasThawHook = isJust . annexThawContentCommand <$> Annex.getGitConfig
+hasThawHook =
+       (isJust . annexThawContentCommand <$> Annex.getGitConfig)
+               <||>
+       (doesAnnexHookExist thawContentAnnexHook)
 
 freezeHook :: RawFilePath -> Annex ()
-freezeHook p = maybe noop go =<< annexFreezeContentCommand <$> Annex.getGitConfig
-  where
-       go basecmd = void $ liftIO $
-               boolSystem "sh" [Param "-c", Param $ gencmd basecmd]
-       gencmd = massReplace [ ("%path", shellEscape (fromRawFilePath p)) ]
+freezeHook = void . runAnnexPathHook "%path"
+       freezeContentAnnexHook annexFreezeContentCommand
 
 thawHook :: RawFilePath -> Annex ()
-thawHook p = maybe noop go =<< annexThawContentCommand <$> Annex.getGitConfig
-  where
-       go basecmd = void $ liftIO $
-               boolSystem "sh" [Param "-c", Param $ gencmd basecmd]
-       gencmd = massReplace [ ("%path", shellEscape (fromRawFilePath p)) ]
+thawHook = void . runAnnexPathHook "%path"
+       thawContentAnnexHook annexThawContentCommand
 
 {- Calculate mode to use for a directory from the mode to use for a file.
  -
index 2f12a10768513fe486be4c738d3c6d6888bb578e..e796b314b973fcc66f8ed6befaa39a7dce24978e 100644 (file)
@@ -35,6 +35,7 @@ import Annex.Common
 import qualified Annex
 import qualified Utility.Url as U
 import qualified Utility.Url.Parse as U
+import Annex.Hook
 import Utility.Hash (IncrementalVerifier)
 import Utility.IPAddress
 import Network.HTTP.Client.Restricted
@@ -75,9 +76,11 @@ getUrlOptions = Annex.getState Annex.urloptions >>= \case
                        <*> pure (Just (\u -> "Configuration of annex.security.allowed-url-schemes does not allow accessing " ++ show u))
                        <*> pure U.noBasicAuth
        
-       headers = annexHttpHeadersCommand <$> Annex.getGitConfig >>= \case
-               Just cmd -> lines <$> liftIO (readProcess "sh" ["-c", cmd])
-               Nothing -> annexHttpHeaders <$> Annex.getGitConfig
+       headers =
+               outputOfAnnexHook httpHeadersAnnexHook annexHttpHeadersCommand
+                       >>= \case
+                               Just output -> pure (lines output)
+                               Nothing -> annexHttpHeaders <$> Annex.getGitConfig
        
        checkallowedaddr = words . annexAllowedIPAddresses <$> Annex.getGitConfig >>= \case
                ["all"] -> do
index 42bcfa6fb12edcb1d9e531445072f7d61855fed7..101fb7d0e2d0f68db69716145125169807ac8526 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -11,9 +11,17 @@ git-annex (10.20250103) UNRELEASED; urgency=medium
   * git-remote-annex: Use enableremote rather than initremote.
   * Windows: Fix permission denied error when dropping files that
     have the readonly attribute set.
+  * Added freezecontent-annex and thawcontent-annex hooks that 
+    correspond to the git configs annex.freezecontent and
+    annex.thawcontent.
+  * Added secure-erase-annex hook that corresponds to the git config
+    annex.secure-erase-command.
+  * Added commitmessage-annex hook that corresponds to the git config
+    annex.commitmessage-command.
+  * Added http-headers-annex hook that corresponds to the git config
+    annex.http-headers-command.
   * Added git configs annex.post-update-command and annex.pre-commit-command
-    that correspond to the git-annex hook scripts post-update-annex and 
-    pre-commit-annex.
+    that correspond to the post-update-annex and pre-commit-annex hooks.
 
  -- Joey Hess <id@joeyh.name>  Fri, 03 Jan 2025 14:30:38 -0400
 
index 1301ec21808a0c0a0547b58c46fa773dcda56991..1163f1effe4a5bf9bc5a5cdab0b08f794b2e0e16 100644 (file)
@@ -108,8 +108,8 @@ hookExists h r = do
                doesFileExist f
 #endif
 
-runHook :: Hook -> Repo -> IO Bool
-runHook h r = do
+runHook :: (FilePath -> [CommandParam] -> IO a) -> Hook -> [CommandParam] -> Repo -> IO a
+runHook runner h ps r = do
        let f = hookFile h r
-       (c, ps) <- findShellCommand f
-       boolSystem c ps
+       (c, cps) <- findShellCommand f
+       runner c (cps ++ ps)
index 1f499cb315078822e02f7c0e0cefcdbc86647b52..81b6dc172729bb48360f532e72a392f63894c41c 100644 (file)
@@ -1154,17 +1154,20 @@ repository, using [[git-annex-config]]. See its man page for a list.)
 
   This command is run and its output is used as the commit message to the
   git-annex branch.
+  
+  Alternatively, a hook script can be installed in 
+  `.git/hooks/commitmessage-annex`
 
 * `annex.post-update-command`
 
   This command is run after git-annex updates the git-annex branch.
 
-  Alternatively, a hook script can be installed in 
-  `.git/hooks/post-update-annex`
-
   When publishing a git-annex repository by http, this can be used to run
   `git update-server-info`
 
+  Alternatively, a hook script can be installed in 
+  `.git/hooks/post-update-annex`
+
 * `annex.pre-commit-command`
 
   This command is run whenever a commit is made to the HEAD branch of
@@ -1422,21 +1425,28 @@ repository, using [[git-annex-config]]. See its man page for a list.)
   erased.
 
   For example, to use the wipe command, set it to `wipe -f %file`.
+  
+  Alternatively to setting the git config, a hook script can be installed
+  in `.git/hooks/secure-erase-annex`
 
 * `annex.freezecontent-command`, `annex.thawcontent-command`
 
   Usually the write permission bits are unset to protect annexed objects
-  from being modified or deleted. The freezecontent-command is run after
+  from being modified or deleted. Freezecontent is run after
   git-annex has removed (or attempted to remove) the write bit, and can
   be used to prevent writing in some other way.
-  The thawcontent-command should undo its effect, and is run before
+  Tawcontent should undo its effect, and is run before
   git-annex restores the write bit.
 
   In the command line, %path is replaced with the file or directory to
   operate on.
 
   (When annex.crippledfilesystem is set, git-annex will not try to
-  remove/restore the write bit, but it will still run these hooks.)
+  remove/restore the write bit, but it will still run freezecontent and
+  thawcontent.)
+
+  Alternatively to setting the git config, hook scripts can be installed
+  in `.git/hooks/freezecontent-annex` and `.git/hooks/thawcontent-annex`.
 
 * `annex.tune.objecthash1`, `annex.tune.objecthashlower`, `annex.tune.branchhash1`
 
@@ -2065,6 +2075,9 @@ Remotes are configured using these settings in `.git/config`.
 
   If set, the command is run and each line of its output is used as a HTTP
   header. This overrides annex.http-headers.
+  
+  Alternatively, a hook script can be installed in 
+  `.git/hooks/http-headers-annex`
 
 * `annex.security.allowed-url-schemes`
 
index f6a51fb30f99b29c5f5780af6f54ca2be7c3b1cd..899b8a69945e0a62eceace33f85fef622ad47da5 100644 (file)
@@ -25,3 +25,5 @@ I wonder if there could be a way added to be able to specify them relative to th
 
 [[!meta author=yoh]]
 [[!tag projects/repronim]]
+
+> [[done]] --[[Joey]]
index 5b5c5d12feae2f7fa1999815bb321a85743364b3..8bb1abd1ddb3356b9f97d5c88c2ca505fc5f9ebe 100644 (file)
@@ -23,12 +23,11 @@ hook that git-annex writes. But, it seems worth having the git config just
 for consistency.
 
 There are some things like annex.youtube-dl-command and
-annex.http-headers-command that are configuring commands for git-annex to
+annex.shared-sop-command that are configuring commands for git-annex to
 run, and are not really hooks per se.
 
 And it does not make sense to have hook scripts that a specific to a given
 remote corresponding to configs like `remote.name.annex-cost-command`.
-Instead it might make sense to have a `.git/hooks/remote-cost-annex` that
-is passed the name of the remote, but that bridge can be crossed if we
-come to it.
+Instead there could be a single `.git/hooks/remote-cost-annex` that
+is passed the name of the remote.
 """]]
diff --git a/doc/todo/specify_freeze__47__thaw_scripts_relative_to_topdir/comment_7_8fd25d5d2b802c6ff6ee0a3802d6f1cb._comment b/doc/todo/specify_freeze__47__thaw_scripts_relative_to_topdir/comment_7_8fd25d5d2b802c6ff6ee0a3802d6f1cb._comment
new file mode 100644 (file)
index 0000000..a40e6ee
--- /dev/null
@@ -0,0 +1,12 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 7"""
+ date="2025-01-10T18:50:07Z"
+ content="""
+Implemented hooks: freezecontent-annex, thawcontent-annex,
+secure-erase-annex, commitmessage-annex, http-headers-annex
+
+That leaves only `remote.name.annex-cost-command` and similar git configs
+that don't have hooks. And a few like annex.youtube-dl-command that are not
+really equivilant to hooks.
+"""]]